home *** CD-ROM | disk | FTP | other *** search
- Title COM - Interrupt Driven Communications Software
- Page 86,132
- include struct.mac
- ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
- ;: ::
- ;: Copyright, 1982, University of B.C. Computing Centre ::
- ;: ::
- ;: Permission to copy without fee all or part of this ::
- ;: material is granted provided that copies are not ::
- ;: made or distributed for direct commercial advantage, ::
- ;: and that this copyright notice is retained in the copy. ::
- ;: ::
- ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-
-
- ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
- ;: ::
- ;: Modified for use with SIMTERM and PASCAL calling sequences ::
- ;: Functions added for controlling and examining line status. ::
- ;: Modified to use the 'structured' macros for easier reading. ::
- ;: ::
- ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-
- Name DRIVERS
-
- ;8250 Asynchronous Communications Element - Registers
- ; The following EQUs assume that the base address is
- ; in BX.
-
- DLH EQU [bx+1] ;Divisor Latch High byte
- DLL EQU [bx] ;Divisor Latch Low byte
- IER EQU [bx+1] ;Interrupt Enable Register
- IIR EQU [bx+2] ;Interrupt Identification Register
- IOR EQU [bx] ;Input Output Register
- LCR EQU [bx+3] ;Line Control Register
- LSR EQU [bx+5] ;Line Status Register
- MCR EQU [bx+4] ;Modem Control Register
- MSR EQU [bx+6] ;Modem Status Register
-
- rs232_base equ 400H ;address of rs232 adapters
-
- ;8259A Priority Interrupt Controller - Registers
-
- INTA0 EQU 20H ;Register 0
- INTA1 EQU 21H ;Register 1
-
- ;8259A Priority Interrupt Controller - Commands
-
- EOI EQU 20H ;Non-specific EOI
- timer_low equ es:[46CH] ; low word of timer
-
- ;COM Flags Mask Bits
-
- F_T_ACT EQU 00000001B ;Transmitter Active
- ;F_BREAK EQU 00000010B ;Break sequence started
- F_XCONT EQU 00000100B ;XON/XOFF control enabled
- F_XOFFR EQU 00001000B ;XOFF recieved/don't transmit
- F_XOFFT EQU 00010000B ;XOFF transmitted/drain buffer
- F_SXOFF EQU 00100000B ;Send XOFF
- F_SXON EQU 01000000B ;Send XON
-
- ;Constants
-
- RBUF_SIZE EQU 3000 ;Receive buffer size
- FENCE_1 EQU rbuf_size/4 ;Send XON fence
- FENCE_2 EQU 1000 ;Send XOFF fence
- SET_BRK EQU 01000000B ;Set-Break flag of LCR
- thre equ 00100000b ; THRE in LSR
- XOFF EQU 00010011B ;ASCII DC3
- XON EQU 00010001B ;ASCII DC1
-
- ;ERR_FLAG masks
-
- chg_lsr equ 00001B ; change in LSR
- chg_msr equ 00010B ; change in MSR
- buf_ful equ 00100B ; receive buffer full
- trans_error equ 01000B ; got an xmit interrupt when we should not have
- not_xmitted equ 10000B ; could not transmit character
-
- data segment public 'data'
- public err_flag
- err_flag db 0 ; record any error conditions
- public lsr_value
- lsr_value db ? ; LSR at change
- public msr_value
- msr_value db ? ; MSR at change
- public recv_reset
- recv_reset db 0 ; flag to reset receiver buffer on BREAK
- data ends
- dgroup group data
-
- Communications SEGMENT PARA PUBLIC 'CODE'
-
- com_base dw ? ; base address of the Async Port
- irq_num db ? ; user specified IRQ
- int_base dw ? ; interrupt @ corresonding to IRQ
- ds_pascal dw ? ; DS on entry (PASCAL Data segment)
- CIVA DW ? ;Comm Interrupt Vector Address
- CIVS DW ? ;Comm Interrupt Vector Segment
- FLAGS DB ? ;COM state Flags
- intr_count dw 0 ; count # of double interrupts
- R_INDEX DW ? ;Recieve buffer index pointer
- R_length DW ? ;Recieve buffer length
- R_BUFF DB rbuf_size DUP(?) ;Recieve character Buffer
- r_buff_end equ $-1
-
-
-
-
- PUBLIC COM_Begin,COM_End
- PUBLIC breaker
- PUBLIC COM_Get
- PUBLIC send
-
- ;---------------------------------------
- ; Setup the interrupt code
- ;---------------------------------------
-
-
- COM_Begin PROC FAR
- ASSUME CS:Communications,DS:nothing
-
- ;procedure COM_Begin( Port : word; [bp+12] (IRQ [bp+13])
- ; Speed : word; [bp+10]
- ; LCR : word; [bp+8]
- ; X_Control : boolean ); [bp+6]
-
- PUSH BP ;Save frame pointer
- MOV BP,SP ;Get new frame pointer
- mov ax,ds
- mov ds_pascal,ax ; Save DS to PASCAL data segment
- push ds
- mov ax,cs ;setup DS addressability
- mov ds,ax
- assume ds:communications
- XOR AX,AX ;Clear AX
- MOV ES,AX ;Address bottom of memory
- MOV FLAGS,AL ;Initialize state flags
- MOV R_length,AX ;Zero Receive buffer Length
- MOV R_INDEX,OFFSET R_BUFF
- mov si,[bp+12] ; com port #
- and si,0FFH ; mask off the port#
- shl si,1
- mov bx,es:rs232_base[si] ; pickup the base address
- mov com_base,bx ; save for the rest of the program
- MOV AL,10000000B ;Set Divisor-Latch bit
- lea dx,lcr
- OUT DX,AL ;Write LCR
- MOV AX,[BP+10] ;Get Bit Rate Divisor
- lea dx,dll
- OUT DX,AL ;Write Divisor Latch Low byte
- MOV AL,AH ;Get high part of divisor
- lea DX,DLH
- OUT DX,AL ;Write Divisor Latch High byte
-
- MOV AL,[BP+8] ;Get Line Control Register settings
- AND AL,00111111B ;Mask out unwanted stuff
- lea DX,LCR
- OUT DX,AL ;Write LCR
-
- .if <byte ptr [bp+6]> ne 0
- OR FLAGS,F_XCONT ;Turn on XON/XOFF control
- .endif
-
- mov al,[bp+13] ; user specified IRQ
- xor ah,ah ; clear high byte
- shl ax,1
- shl ax,1 ; X 4
- mov si,ax
- add si,20H ; base of 8259A interrupts
- mov int_base,si ; save it
-
- CLI ;Disable interrupts
-
- MOV AX,ES:[si+2] ;Get CIV segment
- MOV CIVS,AX ;Save CIVS
- MOV AX,ES:[si] ;Get CIV address
- MOV CIVA,AX ;Save CIVA
- MOV ES:[si+2],CS ;Patch Interrupt Vector
- MOV WORD PTR ES:[si],OFFSET COM_I
-
- STI ;Enable interrupts
-
- MOV AL,00001011B ;Modem Control Register Settings
- lea DX,MCR
- OUT DX,AL ;Write MCR
-
- lea DX,MSR ;Get address of Modem Status Register
- IN AL,DX ;Get Modem Status
-
- lea DX,LSR ;Get address of Line Status Register
- IN AL,DX ;Get Line Status
-
- lea DX,IOR ;Get address of I/O Register
- IN AL,DX ;Get spurious reciever data
-
- MOV AL,00001101B ;Interrupt Enable Register Settings
- ; Don't enable the THRE interrupt
- lea DX,IER
- OUT DX,AL ;Write IER
-
- mov cl,[bp+13] ; user specified IRQ
- mov ch,1 ; bit for mask
- shl ch,cl ; shift mask to proper position
- mov irq_num,ch ; save it
- not ch ; complement
- IN AL,INTA1 ;Get IRQ mask from 8259A
- AND AL,ch ;Mask out IRQ4 bit
- OUT INTA1,AL ;Enable IRQ4
-
- pop ds
- POP BP ;Restore Frame Pointer
- RET 8 ;Return to caller
-
- COM_Begin ENDP
-
- ;---------------------------------------
- ; reset to original condition before exit
- ;---------------------------------------
-
- COM_End PROC FAR
-
- ;procedure COM_End;
-
- push ds
- mov ax,cs ;setup addressability for DS
- mov ds,ax
- mov bx,com_base
- XOR AX,AX ;Clear AX
-
- lea DX,IER
- OUT DX,AL ;Turn off comm interrupts
-
- IN AL,INTA1 ;Get 8259A IRQ mask
- OR AL,irq_num ;Mask in IRQ4 bit
- OUT INTA1,AL ;Disable IRQ4
-
- XOR AX,AX ;Clear AX
- MOV ES,AX ;Address bottom of mermory
-
- CLI ;Disable interrupts
-
- mov si,int_base
- MOV AX,CIVS ;Restore Comm Interrupt Vector
- MOV ES:[si+2],AX
- MOV AX,CIVA
- MOV ES:[si],AX
-
- STI ;Enable interrupts
-
- pop ds
- RET ;Return to caller
-
- COM_End ENDP
- ;---------------------------------------
- ; interrupt code
- ;---------------------------------------
-
- COM_I PROC FAR ;COM Interrupt service routine
- ;--------------------- removed to `cure' the overrun problem ------------+
- ; STI ;Allow further interrupts |
- ;------------------------------------------------------------------------+
- PUSH AX
- PUSH BX
- PUSH DX
- PUSH DI
- PUSH DS
-
- mov ax,cs ;setup addressability for DS
- mov ds,ax
- mov bx,com_base
- dec intr_count
- ;
- ; loop back here to see if any move interrupts are pending
- ;
- retry: lea DX,IIR
- IN AL,DX ;Get class of interrupt
- .ifc al,1 *LONG* ; interrupt pending
- inc intr_count
- xor ah,ah ; clear high bits
- mov di,ax
- jmp [intr_type+di]
-
- ; --------------------
- ; Interuupt Jump Table
- ; --------------------
- intr_type dw modem_intr
- dw trans_intr ; should not get here!!
- dw recv_intr
- dw status_intr
-
- ; ---------------------
- ; LINE STATUS INTERRUPT
- ; ---------------------
- status_intr:
- push ds
- mov ax,ds_pascal
- mov ds,ax
- assume ds:dgroup
- lea DX,LSR ;Get line status
- IN AL,DX
- mov lsr_value,al ;save value at change
- or err_flag,chg_lsr
- pop ds
- assume ds:communications
- JMP retry
-
- ; ----------------------------
- ; RECEIVE DATA READY INTERRUPT
- ; ----------------------------
- recv_intr:
- lea DX,IOR ;Get address of I/O Register
- IN AL,DX ;Get recieved byte
- .ifs flags,f_xcont ; XON/XOFF control
- mov ah,al ; mask off the parity
- and ah,7FH ; bit on the character
- .if ah e xoff
- OR FLAGS,F_XOFFR ;To stop transmitter
- JMP retry
- .endif
-
- .if ah e xon ; XON received
- AND FLAGS,NOT F_XOFFR ;To release transmitter
- JMP retry
- .endif
-
- .if r_length g <size r_buff - fence_2> ; if recv buffer is full
- .ifc flags,f_xofft ; and XOFF not transmitted
- .ifs flags,f_t_act ; xmitter not active
- or flags,f_sxoff+f_xofft ; send XOFF
- .else
- or flags,f_xofft
- push ax
- mov al,xoff ; send the XOFF character
- call send_char
- pop ax
- .endif
- .endif
- .endif
- .endif
-
- .if r_length ge <size r_buff> ;if buffer full, ignore character
- push ds
- mov ax,ds_pascal
- mov ds,ax
- assume ds:dgroup
- or err_flag,buf_ful
- pop ds
- assume ds:communications
- jmp retry
- .endif
-
- MOV DI,R_INDEX ;Get pointer into R_BUFF
- MOV [DI],AL ;Put new byte into buffer
- INC R_length ;Increase buffer length by 1 byte
- inc r_index
- .if di e <offset r_buff_end> ;check for wrap around
- mov r_index,offset r_buff
- .endif
- jmp retry
-
- ; ---------------------
- ; TRANSMITTER INTERRUPT (Should never get here. If we do, exit gracefully)
- ; ---------------------
-
- trans_intr:
- push ds
- mov ax,ds_pascal
- mov ds,ax
- assume ds:dgroup
- or err_flag,trans_error
- pop ds
- assume ds:communications
- jmp retry
-
- ; ----------------------
- ; MODEM STATUS INTERRUPT
- ; ----------------------
- modem_intr:
- push ds
- mov ax,cs:ds_pascal
- mov ds,ax
- assume ds:dgroup
- lea DX,MSR ;Get modem status
- IN AL,DX
- mov msr_value,al
- or err_flag,chg_msr
- pop ds
- assume ds:communications
- jmp retry ; loop until all interrupts are cleared
- .endif
-
- ; CLI ;Turn off interrupts
- MOV AL,EOI ;End Of Interrupt command
- OUT INTA0,AL ;Signal 8259A of EOI
- POP DS
- POP DI
- POP DX
- POP BX
- POP AX
- IRET
-
- COM_I ENDP
-
- ;---------------------------------------
- ; Generate BREAK
- ;---------------------------------------
-
- breaker PROC FAR
- assume ds:nothing
-
- ;procedure COM_Break;
- mov bx,com_base
- cli ; lock out interrupts
- lea dx,lcr
- IN AL,DX ;Get LCR settings
- OR AL,SET_BRK ;Set set-break flag
- OUT DX,AL ;Start break sequence
- and flags,not f_t_act+f_xoffr ; mark xmitter `not active'
- assume es:nothing
- sti
- XOR AX,AX ;Clear AX
- mov es,ax ; ES points to low core
- mov ax,timer_low ; pickup current time
- .repeat ; wait for time to expire
- mov cx,timer_low
- sub cx,ax
- .until cx a 2
- cli
- in al,dx
- and al,not set_brk ; stop the break sequence
- out dx,al
- mov ax,ds_pascal ; setup ES->PASCAL data segment
- mov es,ax
- assume es:dgroup
- .if recv_reset ne 0
- mov r_length,0 ; reset the receiver buffer
- .endif
- assume es:nothing
- sti
- RET ;Return to caller
-
- breaker ENDP
-
-
- ;---------------------------------------
- ; clear the receiver buffer
- ;---------------------------------------
- public clear_receiver
- clear_receiver proc far
- assume ds:nothing
- mov ax,ds_pascal
- mov es,ax
- assume es:dgroup
- .if recv_reset ne 0
- cli
- mov r_length,0 ; clear the recv buffer
- sti
- .endif
- assume es:nothing
- ret
- clear_receiver endp
-
- ;---------------------------------------
- ; Get character from receive buffer
- ;---------------------------------------
-
- COM_Get PROC FAR
- assume ds:nothing
-
- ;function COM_Get( var Character : char ) : boolean
-
- .if r_length le 0 ; receive buffer is empty
- MOV AX,1 ;Return buffer empty code to caller
- RET 2
- .endif
-
- PUSH BP ;Save frame pointer
- MOV BP,SP ;Get new frame pointer
-
- push ds
- mov ax,ds ;setup ES to point to PASCAL data space
- mov es,ax
-
- mov ax,cs ;setup addressability for DS
- mov ds,ax
- assume ds:communications
- mov bx,com_base
- CLI ;Disable interrupts
- MOV SI,R_INDEX ;Get pointer into recieve buffer
- SUB SI,R_length ;Subtract length of buffer
- .if si l <offset r_buff> ; check if pointer needs adjustment
- add si,size r_buff
- .endif
-
- MOV AL,[SI] ;Get character from buffer
- DEC R_length ;Reduce size of buffer by 1 byte
- MOV DI,[BP+6] ;Get address of parameter
- MOV es:[DI],AL ;Store result of COM Get
- .ifs flags,f_xcont ;XON/XOFF control specified
- .ifs flags,f_xofft ;XOFF was transmitted
- .if r_length le fence_1 ;buffer is below the low water mark
- AND FLAGS,NOT F_XOFFT ;Clear XOFF-transmitted flag
- call set_active ; set XMIT active
- mov al,xon ; send XON
- call send_char
- call clear_active ; clear XMIT active
- .endif
- .endif
- .endif
-
- XOR AX,AX ;Return O.K. code to caller
- STI ;Enable interrupts
- pop ds
- POP BP ;Restore frame pointer
- RET 2 ;Return to caller
-
- COM_Get ENDP
-
-
- ;---------------------------------------
- ; Send a character
- ;---------------------------------------
-
- send PROC FAR
-
- ;function send( Buffer : lstring ) : integer;
- ; 8-size, 6-@
-
- PUSH BP ;Save frame pointer
- MOV BP,SP ;Get new frame pointer
- mov ax,ds ;save DS in ES
- mov es,ax
- push ds
- mov ax,cs ;setup addressability for DS
- mov ds,ax
- assume ds:communications
- mov bx,com_base
- MOV CX,[BP+8] ;Get length of string to write
- .if cx g 0
- mov si,[bp+6] ; @ of output string
- .repeat
- call set_active
- mov al,es:[si] ; pickup data from PASCAL
- call send_char
- inc si
- call clear_active
- .until loop
- .endif
- mov ax,-1 ; return OK status
- pop ds
- POP BP ;Restore frame pointer
- RET 4 ;Return to caller
-
- send ENDP
-
- ;---------------------------------------
- ; Set the transmitter active flag
- ;---------------------------------------
- set_active proc near
- cli
- or flags,f_t_act
- sti
- ret
- set_active endp
-
-
- ;---------------------------------------
- ; clear the xmit active flag and check for special conditions
- ;---------------------------------------
- clear_active proc near
- cli
- .ifs flags,f_sxoff ; should we send an XOFF
- and flags,not f_sxoff
- sti
- push ax
- mov al,xoff
- call send_char
- pop ax
- jmp clear_active ; try again
- .endif
- and flags,not f_t_act
- sti
- ret
- clear_active endp
-
- ;---------------------------------------
- ; Send a character (checking for time outs and such)
- ;---------------------------------------
- wait_count equ 36 ; wait at most 2 seconds
- send_char proc near
- push cx
- push dx
- .ifs flags,f_xoffr ; check for xmit hold
- xor cx,cx
- mov es,cx ; address low core for the timer
- mov dx,timer_low ; pickup current time
- .repeat
- .ifc flags,f_xoffr ; terminate if cleared
- mov cx,wait_count+1
- .else
- mov cx,timer_low
- sub cx,dx
- .endif
- .until cx a wait_count ; wait at most 2 seconds
- and flags,not f_xoffr ; clear the wait flag and just proceed
- .endif
- xor cx,cx ; setup wait counter
- lea dx,lsr
- mov ah,al ; save character to xmit
- .repeat
- in al,dx
- .ifs al,thre ; THRE is empty
- mov al,ah
- lea dx,ior
- out dx,al
- pop dx
- pop cx
- ret
- .endif
- .until loop
- push ds
- mov ax,ds_pascal
- mov ds,ax
- assume ds:dgroup
- or err_flag,not_xmitted
- pop ds
- assume ds:communications
- pop dx
- pop cx
- ret
- send_char endp
-
-
- ;---------------------------------------
- ; get/set XON/XOFF status
- ;---------------------------------------
-
- ;
- ; function x_cont(new : boolean) : boolean
- ; sets the new value for XON/XOFF control and returns the OLD value
- ;
- public x_cont
- assume ds:nothing
- x_cont proc far
- push bp
- mov bp,sp
- xor ax,ax ;set to FALSE
- cli
- .ifs flags,f_xcont
- inc ax ;set to TRUE
- .endif
- and flags,not f_xcont ;clear the flag
- .if <byte ptr [bp+6]> ne 0
- or flags,f_xcont
- .endif
- sti
- pop bp
- ret 2
- x_cont endp
-
-
- ;---------------------------------------
- ; procedure clearterm;
- ; Clear the interrupt so stray data will not kill the system
- ;---------------------------------------
- public clearterm
- clearterm proc far
- mov bx,com_base
- lea dx,mcr ; MCR
- in al,dx
- and al,not 1000B ; clear interrupt control bit
- out dx,al
- lea dx,ier ; IER
- xor al,al
- out dx,al ; clear all interrupts
- ret
- clearterm endp
-
- ;---------------------------------------
- ; procedure dropline
- ;
- ; 'drop' the communications line
- ;---------------------------------------
- public dropline
- dropline proc far
- mov bx,com_base
- lea dx,mcr
- in al,dx
- and al,0f0H ; clear the DTR and interrupt flags
- out dx,al
- ret
- dropline endp
-
- ;---------------------------------------
- ; toggle TR -- for VENTEL autodialler
- ;---------------------------------------
- public toggle_tr
-
- toggle_tr proc far
- mov bx,com_base
- xor ax,ax
- mov es,ax ; set ES to low memory
- lea dx,mcr
- in al,dx
- push ax ; save original value
- and al,0fcH ; clear the DTR and interrupt flags
- out dx,al
- mov ax,timer_low
- .repeat
- mov cx,timer_low
- sub cx,ax
- .until cx ae 18 ; wait 1 second
- pop ax ; retrieve original value
- out dx,al
- ret
- toggle_tr endp
-
- ;---------------------------------------
- ; function modem_status : byte;
- ;
- ; returns the contents of the modem status register
- ;---------------------------------------
- public modem_status
- modem_status proc far
- mov bx,com_base
- lea dx,msr
- in al,dx
- ret
- modem_status endp
-
- ;---------------------------------------
- ; procedure clear_comm
- ;
- ; this resets the comm software. Will hopefully unlock any `funny' conditions
- ;---------------------------------------
-
- assume ds:nothing
- public clear_comm
- clear_comm proc far
- cli ; lockout interrupts
- and flags,f_xcont ; leave the XON/XOFF set correctly
- mov r_length,0
- mov r_index,offset r_buff
- sti
- ret
- clear_comm endp
-
-
- ;---------------------------------------
- ; procedure set_interrupt_high(number:integer);
- ;
- ; This causes the communications port to have the highest priority so
- ; that data overruns do not occur.
- ;---------------------------------------
- assume ds:nothing
- public set_interrupt_high
- set_interrupt_high proc far
- push bp
- mov bp,sp
- mov al,[bp+6] ; pick up the interrupt level
- add al,0C0H-1 ; setup the opcode for the 8259
- out 20H,al ; do the operation
- pop bp
- ret 2
- set_interrupt_high endp
-
- Communications ENDS
-
- check$
- END